package com.coderman.daoclient.connections;

import com.coderman.daoclient.bean.KVPair;
import com.coderman.daoclient.reflect.BeanMapInfo;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.util.List;
import java.util.Map;

/**
 * Created by ${fanchunshuai} on 2017-7-27.
 *
 * @version: V1.0
 * @Desc:
 * @Copyright (c) 2017 58到家-版权所有
 */
public abstract  class AbstractStatementTask<T,I> implements StatementFactory {

    private ThreadLocal<Connection> connectionThreadLocal = new ThreadLocal<>();
    private Logger logger = LoggerFactory.getLogger(this.getClass());

    AbstractStatementTask(){
        try {
            BeanMapInfo.initBean();
        } catch (Exception e) {
            logger.error("init bean error",e);
        }
    }
    /**
     * 内部类，实现单例模式
     */
    private static class SingletonHolder {

        private static final ConnectionPool connectionPool = new ConnectionPool();
    }
    /**
     * 单例模式获取实例
     * @return
     */
    private static ConnectionPool singleton() {
        return SingletonHolder.connectionPool;
    }

    @Override
    public PreparedStatement createPreparedStatement(String exeSql) throws SQLException {

        //PreparedStatement preparedStatement  = (PreparedStatement)singleton().getConnection().createStatement();
        initConnection();

        PreparedStatement preparedStatement =  connectionThreadLocal.get().prepareStatement(exeSql);
        return preparedStatement;
    }

    @Override
    public PreparedStatement createPreparedStatement(String exeSql,int generateOrder) throws SQLException {

        //PreparedStatement preparedStatement  = (PreparedStatement)singleton().getConnection().createStatement();
        initConnection();

        PreparedStatement preparedStatement =  connectionThreadLocal.get().prepareStatement(exeSql,generateOrder);
        return preparedStatement;
    }

    /**
     * 以对象的方式插入一条记录db
     * @param t
     * @return 当前插入的行记录id
     * @throws Exception 返回-1L or 0L
     */
    public abstract Long exeInsert(T t) throws Exception;

    /**
     * 以对象的方式修改记录
     * 默认根据主键id删除
     * @param t
     * @throws Exception
     */
    public abstract void exeUpdate(T t) throws Exception;

    /**
     * 以对象的方式修改记录
     * 根据column-value的方式修改
     * @param clazzBean  对象class
     * @param list  修改的列和值
     * @param idPair  id主键的名称和值
     * @throws Exception
     */
    public abstract void exeUpdate(Class clazzBean, List<KVPair> list,KVPair idPair)throws Exception;


    /**
     * 以对象的方式修改记录
     * 根据column-value的方式修改
     * @param t
     * @param list
     * @throws Exception
     */
    public abstract void exeUpdate(T t, List<KVPair> list)throws Exception;

    /**
     * 以对象的方式删除记录
     * @param clazz
     * @throws Exception
     */
    public abstract void exeDelete(Class<?> clazz,I id) throws Exception;

    /**
     * 根据主键id查询记录
     * @param clazz
     * @throws Exception
     */
    public abstract <T> T exeQueryTID(Class<?> clazz,I id) throws Exception;


    /**
     * 以对象的方式查询总条数
     * @param clazz
     * @return
     * @throws Exception
     */
    public abstract Long exeQueryCount(Class<?> clazz) throws Exception;

    /**
     * 以对象的方式查询总条数
     * @param clazz 要查询的对象
     * @param list 查询参数列表
     * @return
     * @throws Exception
     */
    public abstract Long exeQueryCount(Class<?> clazz,List<KVPair> list) throws Exception;


    /**
     * 根据条件查询信息
     * @param clazzBean
     * @param list
     * @param <T>
     * @return
     * @throws Exception
     */
    public abstract <T> List<T> getList(Class<T> clazzBean,List<KVPair> list) throws Exception;

    /**
     * 根据sql查询，返回对象列表
     * @param clazzBean
     * @param sql
     * @param <T>
     * @return
     */
    public abstract <T> List<T> getList(Class<T> clazzBean,String sql) throws Exception;

    /**
     * 根据两个字段获取map形式的数据结构
      * @param keyColumn
     * @param valueColumn
     * @return
     * @throws Exception
     */
    public abstract Map<String,String> getMap(Class<T> clazzBean,String keyColumn,String valueColumn) throws Exception;


    /**
     * 初始化连接
     */
    private void initConnection(){

        if(connectionThreadLocal.get()==null){
            connectionThreadLocal.set(singleton().getConnection());
            /*synchronized (connectionThreadLocal){
                if(connection ==null){
                    connection = singleton().getConnection();
                }
            }*/
        }
    }

    /**
     * 开启事务
     * @throws SQLException
     */
    public void beginTransaction() throws SQLException {
        initConnection();
        connectionThreadLocal.get().setAutoCommit(false);
    }

    /**
     * 关闭事务并归还连接到连接池
     * @throws SQLException
     */
    public void commitTransaction() throws SQLException {
        initConnection();
        connectionThreadLocal.get().commit();
        if(!connectionThreadLocal.get().getAutoCommit()){
            connectionThreadLocal.get().setAutoCommit(true);
        }
        SingletonHolder.connectionPool.free(connectionThreadLocal.get());
        connectionThreadLocal.remove();
    }

    /**
     * 回滚事务
     * @throws SQLException
     */
    public void rollBackTransaction() throws SQLException {
        initConnection();
        if(!connectionThreadLocal.get().getAutoCommit()){
            connectionThreadLocal.get().setAutoCommit(true);
        }
        connectionThreadLocal.get().rollback();
        SingletonHolder.connectionPool.free(connectionThreadLocal.get());
        connectionThreadLocal.remove();
    }

}
